Exploratory Data Analysis (EDA) - Generate questions about your data. - Search for answers by visualising, transforming, and modelling your data. - Use what you learn to refine your questions and/or generate new questions.

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ----------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats

You can loosely word these questions as: What type of variation occurs within my variables? What type of covariation occurs between my variables?

Variation is the tendency of the values of a variable to change from measurement to measurement. The best way to understand that pattern is to visualise the distribution of the variable’s values.

diamonds

To examine the distribution of a categorical variable, use a bar chart:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))

diamonds %>% 
  count(cut)

To examine the distribution of a continuous variable, use a histogram:

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat), binwidth = 0.5)

diamonds %>% 
  count(cut_width(carat, 0.5))
smaller <- diamonds %>% 
  filter(carat < 3)
  
ggplot(data = smaller, mapping = aes(x = carat)) +
  geom_histogram(binwidth = 0.1)

ggplot(data = smaller, mapping = aes(x = carat, colour = cut)) +
  geom_freqpoly(binwidth = 0.1)

Some questions that could be asked: - Which values are the most common? Why? - Which values are rare? Why? Does that match your expectations? - Can you see any unusual patterns? What might explain them?

ggplot(data = smaller, mapping = aes(x = carat)) +
  geom_histogram(binwidth = 0.01)

As an example, the histogram above suggests several interesting questions: - Why are there more diamonds at whole carats and common fractions of carats? - Why are there more diamonds slightly to the right of each peak than there are slightly to the left of each peak? - Why are there no diamonds bigger than 3 carats?

Clusters of similar values suggest that subgroups exist in your data. To understand the subgroups, ask: - How are the observations within each cluster similar to each other? - How are the observations in separate clusters different from each other? - How can you explain or describe the clusters? - Why might the appearance of clusters be misleading?

ggplot(data = faithful, mapping = aes(x = eruptions)) + 
  geom_histogram(binwidth = 0.25)

Discover outliers

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5)

Zoom in for outliers:

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
  coord_cartesian(ylim = c(0, 50))

(unusual <- diamonds %>% 
  filter(y < 3 | y > 20) %>% 
  select(price, x, y, z) %>%
  arrange(y))

Unreasonable data, probably error in entering them.

Exercise P90

1

LOOK AT THE DATA FIRST!!!

?diamonds
ggplot(data = diamonds, mapping = aes(x = x)) + 
  geom_histogram(binwidth = 0.25)

ggplot(data = diamonds, mapping = aes(x = y)) + 
  geom_histogram(binwidth = 0.25)

ggplot(data = diamonds, mapping = aes(x = z)) + 
  geom_histogram(binwidth = 0.25)

NA

There are peaks, range: x

library(ggplot2)
ggplot(data = diamonds) + 
  xlim(c(0,10))+
  geom_histogram(binwidth = 0.25, mapping = aes(x = x), fill= "green") +
  geom_histogram(binwidth = 0.25, mapping = aes(x = y), fill = "yellow") +
  geom_histogram(binwidth = 0.25, mapping = aes(x = z), fill = "blue")

xyz_diamonds <- filter(diamonds, x<20, y<20, z<20)
ggplot(data = xyz_diamonds) +
  geom_freqpoly(binwidth = 0.2, mapping = aes(x = x), color = "blue") +
  geom_freqpoly(binwidth = 0.2, mapping = aes(x = y), color = "green") +
  geom_freqpoly(binwidth = 0.2, mapping = aes(x = z), color = "orange")

2

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 100)

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 500)

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 1000)

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 2000)

diamonds_price <- filter(diamonds, price<2000)
ggplot(data = diamonds_price, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 50)

There is a gap at the price around 1500.

3

(diamonds_99<- filter(diamonds, carat == 0.99))
(diamonds_1<- filter(diamonds, carat == 1))
(diamonds_carat <- filter(diamonds, carat == 0.99|carat == 1))
diamonds_carat %>%
  count(cut_width(carat, 0.01))

People tend to make diamonds of 1 carat… Price vs. carat

4

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5) 

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
  coord_cartesian(ylim = c(0, 50))

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
  coord_cartesian(xlim = c(0, 10))

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y)) +
  coord_cartesian(xlim = c(0, 10))

#when you select a interval not good choice for automatic bin number..
ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.2) +
  coord_cartesian(xlim = c(0, 10))

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y)) +
  coord_cartesian(xlim = c(3.5, 4.5))

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram(binwidth = 100)

ggplot(data = diamonds, mapping = aes(x = price))+ 
  geom_histogram()

ggplot(data = diamonds, mapping = aes(x = x))+ 
  geom_histogram()

R automatically picks a value so that there are 30 bins?

For unusual values

diamonds2 <- diamonds %>% 
  mutate(y = ifelse(y < 3 | y > 20, NA, y))
diamonds2

ifelse() has three arguments. The first argument test should be a logical vector. The result will contain the value of the second argument, yes, when test is TRUE, and the value of the third argument, no, when it is false.

ggplot(data = diamonds2, mapping = aes(x = x, y = y)) + 
  geom_point()

#it gives a warning..
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) + 
  geom_point(na.rm = TRUE)

nycflights13::flights %>% 
  mutate(
    cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + sched_min / 60
  ) %>% 
  ggplot(mapping = aes(sched_dep_time)) + 
    geom_freqpoly(mapping = aes(colour = cancelled), binwidth = 1/4)

Exercise P93

1

ggplot(data = diamonds2, mapping = aes(x = y)) + 
  geom_bar()

ggplot(data = diamonds2, mapping = aes(x = y)) + 
  geom_bar(na.rm = TRUE)

ggplot(data = diamonds2, mapping = aes(x = y)) + 
  geom_histogram()

ggplot(data = diamonds2, mapping = aes(x = y)) + 
  geom_histogram(na.rm = TRUE)

2

mean(y)
[1] NA
mean(y, na.rm = TRUE)
[1] 12.63907
sum(y)
[1] NA
sum(y, na.rm = TRUE)
[1] 4152200

Covariation is the tendency for the values of two or more variables to vary together in a related way.

The default appearance of geom_freqpoly() is not that useful for that sort of comparison because the height is given by the count. That means if one of the groups is much smaller than the others, it’s hard to see the differences in shape.

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

#to see overall difference
ggplot(diamonds) + 
  geom_bar(mapping = aes(x = cut))

To display density

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) + 
  geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_boxplot()

ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
  geom_boxplot()

order the data

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
  coord_flip()

Exercise P90

1

nycflights13::flights
nycflights13::flights %>% 
  mutate(
    Cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + sched_min / 60
  ) %>% 
  ggplot(mapping = aes(x = sched_dep_time, y = ..density..)) + 
    geom_freqpoly(mapping = aes(colour = Cancelled), binwidth = 1/4) +
  ylab("Density") +
  xlab("Schedules departure time (hour)")

2

diamonds
?diamonds
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_point() +
  geom_smooth()

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = color, y = price)) +
  geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = clarity, y = price)) +
  geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = depth, y = price)) +
  geom_point() +
  geom_smooth()

ggplot(data = diamonds, mapping = aes(x = table, y = price)) +
  geom_point() +
  geom_smooth()

ggplot(data = xyz_diamonds, mapping = aes(y = price)) +
  geom_point(mapping = aes(x = x), color = "green", alpha = 0.1) +
  geom_point(mapping = aes(x = y), color = "yellow", alpha = 0.1) +
  geom_point(mapping = aes(x = z), color = "black", alpha = 0.1)

The is correlation between carat and size with price, but the most important to predict price seems to be color.

ggplot(data = diamonds, mapping = aes(x = carat, y = price, color = cut)) +
  geom_point()

Diamonds with fair cut but bigger value of carat still get a very high price.There’s few diamonds with ideal cut at bigger carat. And at that range the values seem not correlated. ###3

library(ggstance)

Attaching package: 'ggstance'

The following objects are masked from 'package:ggplot2':

    GeomErrorbarh, geom_errorbarh
ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = class, y = hwy)) +
  coord_flip()

ggplot(data = mpg) +
  geom_boxploth(mapping = aes(x = hwy, y = class))

Exactly the same?? ###4

library(lvplot)
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_lv()
Error: GeomLv was built with an incompatible version of ggproto.
Please reinstall the package that provides this extension.

5

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) + 
  geom_violin()

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_histogram() +
  facet_wrap(~cut)

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) + 
  geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

Violin: see the trend of each category clearly Hist & facet: can also compare the number Frequency: compare shapes directly because they overlap ggbeeswarm

6

library(ggbeeswarm)
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) + 
  geom_point()

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) + 
  geom_jitter()

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
  geom_jitter(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
  geom_beeswarm(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
  geom_beeswarm(mapping = aes(x = displ, y = hwy), groupOnX = F)

Two categorical variables

ggplot(data = diamonds) +
  geom_count(mapping = aes(x = cut, y = color))

diamonds %>% 
  count(color, cut)
diamonds %>% 
  count(color, cut) %>%  
  ggplot(mapping = aes(x = color, y = cut)) +
    geom_tile(mapping = aes(fill = n))

Exercise P101

1

library(viridis)
diamonds %>% 
  count(color, cut) %>%
  group_by(color) %>%
  mutate(prop = n / sum(n)) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(mapping = aes(fill = prop))  +
  scale_fill_viridis(limits = c(0, 1))

2

nycflights13::flights
nycflights13::flights %>% 
  group_by(dest, month) %>%  
  summarise(avg_delay = mean(dep_delay, na.rm = T)) %>%  
  ggplot(mapping = aes(x = dest, y = month)) +
    geom_tile(mapping = aes(fill = avg_delay))

The destination overlaps. Missing values.

nycflights13::flights %>% 
  group_by(dest) %>%  
  summarise(avg_delay = mean(dep_delay, na.rm = T))
nycflights13::flights %>% 
  filter(dest == dest[c(10:20)]) %>%
  group_by(dest, month) %>%  
  summarise(avg_delay = mean(dep_delay, na.rm = T)) %>%  
  ggplot(mapping = aes(x = factor(month), y = dest)) +
    geom_tile(mapping = aes(fill = avg_delay)) +
labs(x = "Month", y = "Destination", fill = "Departure Delay")

3

diamonds %>% 
  count(color, cut) %>%  
  ggplot(mapping = aes(x = cut, y = color)) +
    geom_tile(mapping = aes(fill = n))

diamonds %>% 
  count(color, cut) %>%  
  ggplot(mapping = aes(x = color, y = cut)) +
    geom_tile(mapping = aes(fill = n))

Because colors can not be compared but cut can? So easier to understand…

Two continuous variables

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = carat, y = price))

Data overlaps a lot.

ggplot(data = diamonds) + 
  geom_point(mapping = aes(x = carat, y = price), alpha = 1 / 100)

library(hexbin)
ggplot(data = smaller) +
  geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = smaller) +
  geom_hex(mapping = aes(x = carat, y = price))

ggplot(data = smaller, mapping = aes(x = carat, y = price)) + 
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

Make the width of the boxplot proportional to the number of points

ggplot(data = smaller, mapping = aes(x = carat, y = price)) + 
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)), varwidth = TRUE)

Display approximately the same number of points in each bin.

ggplot(data = smaller, mapping = aes(x = carat, y = price)) + 
  geom_boxplot(mapping = aes(group = cut_number(carat, 20)))

Exercise P104

1

ggplot(data = smaller, mapping = aes(x = price,y = ..density..)) + 
  geom_freqpoly(mapping = aes(color = cut_number(carat, 10)))

ggplot(data = smaller, mapping = aes(x = price, y = ..density..)) + 
  geom_freqpoly(mapping = aes(color = cut_width(carat, 0.5)))

Two methods yeild very different graph. Cut_number same as its density graph. ###2

ggplot(data = smaller, mapping = aes(x = price, y = carat)) + 
  geom_boxplot(mapping = aes(group = cut_number(price, 20)))

ggplot(data = smaller, mapping = aes(x = price, y = carat)) + 
  geom_boxplot(mapping = aes(group = cut_width(price, 1000,boundary = 0)))

3

It seems that when the price is high, the price doesn’t correlate to the size anymore. Maybe some other factors influences.

4

diamonds %>% 
  #filter(carat<3) %>% 
  ggplot(mapping = aes(x = carat, y = cut)) +
    geom_tile(mapping = aes(fill = price))

Price correlate with carat more than cut.

diamonds %>% 
  #filter(carat<3) %>% 
  ggplot(mapping = aes(x = carat, y = price)) +
  geom_point(alpha = 0.5) +
    facet_wrap(~cut)

More diamonds with large size for fair cut. Other than that not much difference between the different cut.

5

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = x, y = y)) +
  coord_cartesian(xlim = c(4, 11), ylim = c(4, 11))

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = x, y = y)) +
  coord_cartesian(xlim = c(4, 11), ylim = c(4, 11))

Because outliers are not shown in binned plots.

Patterns and models

Could this pattern be due to coincidence (i.e. random chance)? How can you describe the relationship implied by the pattern? How strong is the relationship implied by the pattern? What other variables might affect the relationship? Does the relationship change if you look at individual subgroups of the data?

ggplot(data = faithful) + 
  geom_point(mapping = aes(x = eruptions, y = waiting))

If you think of variation as a phenomenon that creates uncertainty, covariation is a phenomenon that reduces it.

library(modelr)
mod <- lm(log(price) ~ log(carat), data = diamonds)
diamonds2 <- diamonds %>% 
  add_residuals(mod) %>% 
  mutate(resid = exp(resid))
ggplot(data = diamonds2) + 
  geom_point(mapping = aes(x = carat, y = resid))

relative to their size, better quality diamonds are more expensive.

ggplot(data = diamonds2) + 
  geom_boxplot(mapping = aes(x = cut, y = resid))

ggplot(data = faithful, mapping = aes(x = eruptions)) + 
  geom_freqpoly(binwidth = 0.25)

ggplot(faithful, aes(eruptions)) + 
  geom_freqpoly(binwidth = 0.25)

http://www.cookbook-r.com/Graphs/

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpCi0gR2VuZXJhdGUgcXVlc3Rpb25zIGFib3V0IHlvdXIgZGF0YS4KLSBTZWFyY2ggZm9yIGFuc3dlcnMgYnkgdmlzdWFsaXNpbmcsIHRyYW5zZm9ybWluZywgYW5kIG1vZGVsbGluZyB5b3VyIGRhdGEuCi0gVXNlIHdoYXQgeW91IGxlYXJuIHRvIHJlZmluZSB5b3VyIHF1ZXN0aW9ucyBhbmQvb3IgZ2VuZXJhdGUgbmV3IHF1ZXN0aW9ucy4KYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKWW91IGNhbiBsb29zZWx5IHdvcmQgdGhlc2UgcXVlc3Rpb25zIGFzOgpXaGF0IHR5cGUgb2YgdmFyaWF0aW9uIG9jY3VycyB3aXRoaW4gbXkgdmFyaWFibGVzPwpXaGF0IHR5cGUgb2YgY292YXJpYXRpb24gb2NjdXJzIGJldHdlZW4gbXkgdmFyaWFibGVzPwoKLSBBIHZhcmlhYmxlIGlzIGEgcXVhbnRpdHksIHF1YWxpdHksIG9yIHByb3BlcnR5IHRoYXQgeW91IGNhbiBtZWFzdXJlLgotIEEgdmFsdWUgaXMgdGhlIHN0YXRlIG9mIGEgdmFyaWFibGUgd2hlbiB5b3UgbWVhc3VyZSBpdC4gVGhlIHZhbHVlIG9mIGEgdmFyaWFibGUgbWF5IGNoYW5nZSBmcm9tIG1lYXN1cmVtZW50IHRvIG1lYXN1cmVtZW50LgotIEFuIG9ic2VydmF0aW9uIGlzIGEgc2V0IG9mIG1lYXN1cmVtZW50cyBtYWRlIHVuZGVyIHNpbWlsYXIgY29uZGl0aW9ucyAoeW91IHVzdWFsbHkgbWFrZSBhbGwgb2YgdGhlIG1lYXN1cmVtZW50cyBpbiBhbiBvYnNlcnZhdGlvbiBhdCB0aGUgc2FtZSB0aW1lIGFuZCBvbiB0aGUgc2FtZSBvYmplY3QpLiBBbiBvYnNlcnZhdGlvbiB3aWxsIGNvbnRhaW4gc2V2ZXJhbCB2YWx1ZXMsIGVhY2ggYXNzb2NpYXRlZCB3aXRoIGEgZGlmZmVyZW50IHZhcmlhYmxlLiBJ4oCZbGwgc29tZXRpbWVzIHJlZmVyIHRvIGFuIG9ic2VydmF0aW9uIGFzIGEgZGF0YSBwb2ludC4KLSBUYWJ1bGFyIGRhdGEgaXMgYSBzZXQgb2YgdmFsdWVzLCBlYWNoIGFzc29jaWF0ZWQgd2l0aCBhIHZhcmlhYmxlIGFuZCBhbiBvYnNlcnZhdGlvbi4gVGFidWxhciBkYXRhIGlzIHRpZHkgaWYgZWFjaCB2YWx1ZSBpcyBwbGFjZWQgaW4gaXRzIG93biDigJxjZWxs4oCdLCBlYWNoIHZhcmlhYmxlIGluIGl0cyBvd24gY29sdW1uLCBhbmQgZWFjaCBvYnNlcnZhdGlvbiBpbiBpdHMgb3duIHJvdy4KClZhcmlhdGlvbiBpcyB0aGUgdGVuZGVuY3kgb2YgdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlIHRvIGNoYW5nZSBmcm9tIG1lYXN1cmVtZW50IHRvIG1lYXN1cmVtZW50LiBUaGUgYmVzdCB3YXkgdG8gdW5kZXJzdGFuZCB0aGF0IHBhdHRlcm4gaXMgdG8gdmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxl4oCZcyB2YWx1ZXMuCmBgYHtyfQpkaWFtb25kcwpgYGAKClRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB1c2UgYSBiYXIgY2hhcnQ6CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0KSkKYGBgCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY3V0KQpgYGAKVG8gZXhhbWluZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyB2YXJpYWJsZSwgdXNlIGEgaGlzdG9ncmFtOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSwgYmlud2lkdGggPSAwLjUpCmBgYApgYGB7cn0KZGlhbW9uZHMgJT4lIAogIGNvdW50KGN1dF93aWR0aChjYXJhdCwgMC41KSkKYGBgCmBgYHtyfQpzbWFsbGVyIDwtIGRpYW1vbmRzICU+JSAKICBmaWx0ZXIoY2FyYXQgPCAzKQogIApnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlciwgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIGNvbG91ciA9IGN1dCkpICsKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4xKQpgYGAKU29tZSBxdWVzdGlvbnMgdGhhdCBjb3VsZCBiZSBhc2tlZDoKLSBXaGljaCB2YWx1ZXMgYXJlIHRoZSBtb3N0IGNvbW1vbj8gV2h5PwotIFdoaWNoIHZhbHVlcyBhcmUgcmFyZT8gV2h5PyBEb2VzIHRoYXQgbWF0Y2ggeW91ciBleHBlY3RhdGlvbnM/Ci0gQ2FuIHlvdSBzZWUgYW55IHVudXN1YWwgcGF0dGVybnM/IFdoYXQgbWlnaHQgZXhwbGFpbiB0aGVtPwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpCmBgYApBcyBhbiBleGFtcGxlLCB0aGUgaGlzdG9ncmFtIGFib3ZlIHN1Z2dlc3RzIHNldmVyYWwgaW50ZXJlc3RpbmcgcXVlc3Rpb25zOgotIFdoeSBhcmUgdGhlcmUgbW9yZSBkaWFtb25kcyBhdCB3aG9sZSBjYXJhdHMgYW5kIGNvbW1vbiBmcmFjdGlvbnMgb2YgY2FyYXRzPwotIFdoeSBhcmUgdGhlcmUgbW9yZSBkaWFtb25kcyBzbGlnaHRseSB0byB0aGUgcmlnaHQgb2YgZWFjaCBwZWFrIHRoYW4gdGhlcmUgYXJlIHNsaWdodGx5IHRvIHRoZSBsZWZ0IG9mIGVhY2ggcGVhaz8KLSBXaHkgYXJlIHRoZXJlIG5vIGRpYW1vbmRzIGJpZ2dlciB0aGFuIDMgY2FyYXRzPwoKQ2x1c3RlcnMgb2Ygc2ltaWxhciB2YWx1ZXMgc3VnZ2VzdCB0aGF0IHN1Ymdyb3VwcyBleGlzdCBpbiB5b3VyIGRhdGEuIFRvIHVuZGVyc3RhbmQgdGhlIHN1Ymdyb3VwcywgYXNrOgotIEhvdyBhcmUgdGhlIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaCBjbHVzdGVyIHNpbWlsYXIgdG8gZWFjaCBvdGhlcj8KLSBIb3cgYXJlIHRoZSBvYnNlcnZhdGlvbnMgaW4gc2VwYXJhdGUgY2x1c3RlcnMgZGlmZmVyZW50IGZyb20gZWFjaCBvdGhlcj8KLSBIb3cgY2FuIHlvdSBleHBsYWluIG9yIGRlc2NyaWJlIHRoZSBjbHVzdGVycz8KLSBXaHkgbWlnaHQgdGhlIGFwcGVhcmFuY2Ugb2YgY2x1c3RlcnMgYmUgbWlzbGVhZGluZz8KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGZhaXRoZnVsLCBtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSkKYGBgCgpEaXNjb3ZlciBvdXRsaWVycwpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkKYGBgClpvb20gaW4gZm9yIG91dGxpZXJzOgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCA1MCkpCmBgYApgYGB7cn0KKHVudXN1YWwgPC0gZGlhbW9uZHMgJT4lIAogIGZpbHRlcih5IDwgMyB8IHkgPiAyMCkgJT4lIAogIHNlbGVjdChwcmljZSwgeCwgeSwgeikgJT4lCiAgYXJyYW5nZSh5KSkKYGBgClVucmVhc29uYWJsZSBkYXRhLCBwcm9iYWJseSBlcnJvciBpbiBlbnRlcmluZyB0aGVtLgoKIyNFeGVyY2lzZSBQOTAKIyMjMQpMT09LIEFUIFRIRSBEQVRBIEZJUlNUISEhCmBgYHtyfQo/ZGlhbW9uZHMKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geikpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQogIApgYGAKVGhlcmUgYXJlIHBlYWtzLCByYW5nZTogeDx6PHkKcmFuZ2U7IG91dGxpZXJzCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIAogIHhsaW0oYygwLDEwKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1LCBtYXBwaW5nID0gYWVzKHggPSB4KSwgZmlsbD0gImdyZWVuIikgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSwgbWFwcGluZyA9IGFlcyh4ID0geSksIGZpbGwgPSAieWVsbG93IikgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSwgbWFwcGluZyA9IGFlcyh4ID0geiksIGZpbGwgPSAiYmx1ZSIpCmBgYAoKCmBgYHtyfQp4eXpfZGlhbW9uZHMgPC0gZmlsdGVyKGRpYW1vbmRzLCB4PDIwLCB5PDIwLCB6PDIwKQpnZ3Bsb3QoZGF0YSA9IHh5el9kaWFtb25kcykgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjIsIG1hcHBpbmcgPSBhZXMoeCA9IHgpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBjb2xvciA9ICJncmVlbiIpICsKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4yLCBtYXBwaW5nID0gYWVzKHggPSB6KSwgY29sb3IgPSAib3JhbmdlIikKYGBgCiMjIzIKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwMCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMDApCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMDAwKQpgYGAKYGBge3J9CmRpYW1vbmRzX3ByaWNlIDwtIGZpbHRlcihkaWFtb25kcywgcHJpY2U8MjAwMCkKZ2dwbG90KGRhdGEgPSBkaWFtb25kc19wcmljZSwgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTApCmBgYApUaGVyZSBpcyBhIGdhcCBhdCB0aGUgcHJpY2UgYXJvdW5kIDE1MDAuCgojIyMzCmBgYHtyfQooZGlhbW9uZHNfOTk8LSBmaWx0ZXIoZGlhbW9uZHMsIGNhcmF0ID09IDAuOTkpKQooZGlhbW9uZHNfMTwtIGZpbHRlcihkaWFtb25kcywgY2FyYXQgPT0gMSkpCgooZGlhbW9uZHNfY2FyYXQgPC0gZmlsdGVyKGRpYW1vbmRzLCBjYXJhdCA9PSAwLjk5fGNhcmF0ID09IDEpKQpkaWFtb25kc19jYXJhdCAlPiUKICBjb3VudChjdXRfd2lkdGgoY2FyYXQsIDAuMDEpKQpgYGAKUGVvcGxlIHRlbmQgdG8gbWFrZSBkaWFtb25kcyBvZiAxIGNhcmF0Li4uIApQcmljZSB2cy4gY2FyYXQKCiMjIzQKYGBge3J9CmdncGxvdChkaWFtb25kcykgKyAKICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB5KSwgYmlud2lkdGggPSAwLjUpIAoKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCA1MCkpCgpnZ3Bsb3QoZGlhbW9uZHMpICsgCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0geSksIGJpbndpZHRoID0gMC41KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEwKSkKCmdncGxvdChkaWFtb25kcykgKyAKICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB5KSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxMCkpCiN3aGVuIHlvdSBzZWxlY3QgYSBpbnRlcnZhbCBub3QgZ29vZCBjaG9pY2UgZm9yIGF1dG9tYXRpYyBiaW4gbnVtYmVyLi4KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuMikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxMCkpCgoKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDMuNSwgNC41KSkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDApCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpKyAKICBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSB4KSkrIAogIGdlb21faGlzdG9ncmFtKCkKYGBgClIgYXV0b21hdGljYWxseSBwaWNrcyBhIHZhbHVlIHNvIHRoYXQgdGhlcmUgYXJlIDMwIGJpbnM/CgoKRm9yIHVudXN1YWwgdmFsdWVzIApgYGB7cn0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBtdXRhdGUoeSA9IGlmZWxzZSh5IDwgMyB8IHkgPiAyMCwgTkEsIHkpKQpkaWFtb25kczIKYGBgCmlmZWxzZSgpIGhhcyB0aHJlZSBhcmd1bWVudHMuIFRoZSBmaXJzdCBhcmd1bWVudCB0ZXN0IHNob3VsZCBiZSBhIGxvZ2ljYWwgdmVjdG9yLiBUaGUgcmVzdWx0IHdpbGwgY29udGFpbiB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBhcmd1bWVudCwgeWVzLCB3aGVuIHRlc3QgaXMgVFJVRSwgYW5kIHRoZSB2YWx1ZSBvZiB0aGUgdGhpcmQgYXJndW1lbnQsIG5vLCB3aGVuIGl0IGlzIGZhbHNlLgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludCgpCiNpdCBnaXZlcyBhIHdhcm5pbmcuLgpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludChuYS5ybSA9IFRSVUUpCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBtdXRhdGUoCiAgICBjYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksCiAgICBzY2hlZF9ob3VyID0gc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCwKICAgIHNjaGVkX21pbiA9IHNjaGVkX2RlcF90aW1lICUlIDEwMCwKICAgIHNjaGVkX2RlcF90aW1lID0gc2NoZWRfaG91ciArIHNjaGVkX21pbiAvIDYwCiAgKSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoc2NoZWRfZGVwX3RpbWUpKSArIAogICAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNhbmNlbGxlZCksIGJpbndpZHRoID0gMS80KQpgYGAKCiMjRXhlcmNpc2UgUDkzCiMjIzEKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB5KSkgKyAKICBnZW9tX2JhcigpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kczIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArIAogIGdlb21fYmFyKG5hLnJtID0gVFJVRSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kczIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArIAogIGdlb21faGlzdG9ncmFtKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMiwgbWFwcGluZyA9IGFlcyh4ID0geSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0obmEucm0gPSBUUlVFKQpgYGAKIyMjMgpgYGB7cn0KbWVhbih5KQptZWFuKHksIG5hLnJtID0gVFJVRSkKCnN1bSh5KQpzdW0oeSwgbmEucm0gPSBUUlVFKQpgYGAKCgpDb3ZhcmlhdGlvbiBpcyB0aGUgdGVuZGVuY3kgZm9yIHRoZSB2YWx1ZXMgb2YgdHdvIG9yIG1vcmUgdmFyaWFibGVzIHRvIHZhcnkgdG9nZXRoZXIgaW4gYSByZWxhdGVkIHdheS4gCgpUaGUgZGVmYXVsdCBhcHBlYXJhbmNlIG9mIGdlb21fZnJlcXBvbHkoKSBpcyBub3QgdGhhdCB1c2VmdWwgZm9yIHRoYXQgc29ydCBvZiBjb21wYXJpc29uIGJlY2F1c2UgdGhlIGhlaWdodCBpcyBnaXZlbiBieSB0aGUgY291bnQuIFRoYXQgbWVhbnMgaWYgb25lIG9mIHRoZSBncm91cHMgaXMgbXVjaCBzbWFsbGVyIHRoYW4gdGhlIG90aGVycywgaXTigJlzIGhhcmQgdG8gc2VlIHRoZSBkaWZmZXJlbmNlcyBpbiBzaGFwZS4gCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGN1dCksIGJpbndpZHRoID0gNTAwKQoKI3RvIHNlZSBvdmVyYWxsIGRpZmZlcmVuY2UKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGN1dCkpCmBgYApUbyBkaXNwbGF5IGRlbnNpdHkKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgeSA9IGh3eSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKb3JkZXIgdGhlIGRhdGEKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwgeSA9IGh3eSkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjbGFzcywgaHd5LCBGVU4gPSBtZWRpYW4pLCB5ID0gaHd5KSkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKIyNFeGVyY2lzZSBQOTAKIyMjMQpgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBtdXRhdGUoCiAgICBDYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksCiAgICBzY2hlZF9ob3VyID0gc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCwKICAgIHNjaGVkX21pbiA9IHNjaGVkX2RlcF90aW1lICUlIDEwMCwKICAgIHNjaGVkX2RlcF90aW1lID0gc2NoZWRfaG91ciArIHNjaGVkX21pbiAvIDYwCiAgKSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHNjaGVkX2RlcF90aW1lLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogICAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IENhbmNlbGxlZCksIGJpbndpZHRoID0gMS80KSArCiAgeWxhYigiRGVuc2l0eSIpICsKICB4bGFiKCJTY2hlZHVsZXMgZGVwYXJ0dXJlIHRpbWUgKGhvdXIpIikKYGBgCiMjIzIKYGBge3J9CmRpYW1vbmRzCj9kaWFtb25kcwpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY2xhcml0eSwgeSA9IHByaWNlKSkgKwogIGdlb21fYm94cGxvdCgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBkZXB0aCwgeSA9IHByaWNlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gdGFibGUsIHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKCkKCmdncGxvdChkYXRhID0geHl6X2RpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHgpLCBjb2xvciA9ICJncmVlbiIsIGFscGhhID0gMC4xKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB5KSwgY29sb3IgPSAieWVsbG93IiwgYWxwaGEgPSAwLjEpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHopLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4xKQpgYGAKVGhlIGlzIGNvcnJlbGF0aW9uIGJldHdlZW4gY2FyYXQgYW5kIHNpemUgd2l0aCBwcmljZSwgYnV0IHRoZSBtb3N0IGltcG9ydGFudCB0byBwcmVkaWN0IHByaWNlIHNlZW1zIHRvIGJlIGNvbG9yLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sb3IgPSBjdXQpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApEaWFtb25kcyB3aXRoIGZhaXIgY3V0IGJ1dCBiaWdnZXIgdmFsdWUgb2YgY2FyYXQgc3RpbGwgZ2V0IGEgdmVyeSBoaWdoIHByaWNlLlRoZXJlJ3MgZmV3IGRpYW1vbmRzIHdpdGggaWRlYWwgY3V0IGF0IGJpZ2dlciBjYXJhdC4gQW5kIGF0IHRoYXQgcmFuZ2UgdGhlIHZhbHVlcyBzZWVtIG5vdCBjb3JyZWxhdGVkLgojIyMzCmBgYHtyfQpsaWJyYXJ5KGdnc3RhbmNlKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkgKwogIGNvb3JkX2ZsaXAoKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JveHBsb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGh3eSwgeSA9IGNsYXNzKSkKYGBgCkV4YWN0bHkgdGhlIHNhbWU/PwojIyM0CmBgYHtyfQpsaWJyYXJ5KGx2cGxvdCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9sdigpCmBgYAojIyM1CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsgCiAgZ2VvbV92aW9saW4oKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAofmN1dCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgClZpb2xpbjogc2VlIHRoZSB0cmVuZCBvZiBlYWNoIGNhdGVnb3J5IGNsZWFybHkKSGlzdCAmIGZhY2V0OiBjYW4gYWxzbyBjb21wYXJlIHRoZSBudW1iZXIKRnJlcXVlbmN5OiBjb21wYXJlIHNoYXBlcyBkaXJlY3RseSBiZWNhdXNlIHRoZXkgb3ZlcmxhcApnZ2JlZXN3YXJtCgojIyM2CmBgYHtyfQpsaWJyYXJ5KGdnYmVlc3dhcm0pCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fcG9pbnQoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArIAogIGdlb21faml0dGVyKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2ppdHRlcihtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCgpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmVlc3dhcm0obWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JlZXN3YXJtKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSwgZ3JvdXBPblggPSBGKQpgYGAKIyMgVHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21fY291bnQobWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gY29sb3IpKQpgYGAKYGBge3J9CmRpYW1vbmRzICU+JSAKICBjb3VudChjb2xvciwgY3V0KQojZm9yIGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIGNvbG9yIGFuZCBjb3VudApgYGAKYGBge3J9CmRpYW1vbmRzICU+JSAKICBjb3VudChjb2xvciwgY3V0KSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBjb2xvciwgeSA9IGN1dCkpICsKICAgIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBuKSkKYGBgCgojIyBFeGVyY2lzZSBQMTAxCiMjIzEKYGBge3J9CmxpYnJhcnkodmlyaWRpcykKYGBgCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lCiAgZ3JvdXBfYnkoY29sb3IpICU+JQogIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkgJT4lCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0KSkgKwogIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBwcm9wKSkgICsKICBzY2FsZV9maWxsX3ZpcmlkaXMobGltaXRzID0gYygwLCAxKSkKYGBgCiMjIzIKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cwpgYGAKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cyAlPiUgCiAgZ3JvdXBfYnkoZGVzdCwgbW9udGgpICU+JSAgCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFQpKSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBkZXN0LCB5ID0gbW9udGgpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gYXZnX2RlbGF5KSkKYGBgClRoZSBkZXN0aW5hdGlvbiBvdmVybGFwcy4gTWlzc2luZyB2YWx1ZXMuIAoKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cyAlPiUgCiAgZ3JvdXBfYnkoZGVzdCkgJT4lICAKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVCkpCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBmaWx0ZXIoZGVzdCA9PSBkZXN0W2MoMTA6MjApXSkgJT4lCiAgZ3JvdXBfYnkoZGVzdCwgbW9udGgpICU+JSAgCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFQpKSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBmYWN0b3IobW9udGgpLCB5ID0gZGVzdCkpICsKICAgIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBhdmdfZGVsYXkpKSArCmxhYnMoeCA9ICJNb250aCIsIHkgPSAiRGVzdGluYXRpb24iLCBmaWxsID0gIkRlcGFydHVyZSBEZWxheSIpCmBgYAojIyMzCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lICAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gY29sb3IpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpCgpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lICAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBjdXQpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpCmBgYApCZWNhdXNlIGNvbG9ycyBjYW4gbm90IGJlIGNvbXBhcmVkIGJ1dCBjdXQgY2FuPyBTbyBlYXNpZXIgdG8gdW5kZXJzdGFuZC4uLgoKIyNUd28gY29udGludW91cyB2YXJpYWJsZXMKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQpgYGAKRGF0YSBvdmVybGFwcyBhIGxvdC4KYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSwgYWxwaGEgPSAxIC8gMTAwKQpgYGAKYGBge3J9CmxpYnJhcnkoaGV4YmluKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlcikgKwogIGdlb21fYmluMmQobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpCgpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIpICsKICBnZW9tX2hleChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCAwLjEpKSkKYGBgCk1ha2UgdGhlIHdpZHRoIG9mIHRoZSBib3hwbG90IHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIHBvaW50cwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF93aWR0aChjYXJhdCwgMC4xKSksIHZhcndpZHRoID0gVFJVRSkKYGBgCkRpc3BsYXkgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBudW1iZXIgb2YgcG9pbnRzIGluIGVhY2ggYmluLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF9udW1iZXIoY2FyYXQsIDIwKSkpCmBgYAoKIyNFeGVyY2lzZSBQMTA0CiMjIzEKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlciwgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UseSA9IC4uZGVuc2l0eS4uKSkgKyAKICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjdXRfbnVtYmVyKGNhcmF0LCAxMCkpKQoKZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSwgeSA9IC4uZGVuc2l0eS4uKSkgKyAKICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjdXRfd2lkdGgoY2FyYXQsIDAuNSkpKQpgYGAKVHdvIG1ldGhvZHMgeWVpbGQgdmVyeSBkaWZmZXJlbnQgZ3JhcGguIEN1dF9udW1iZXIgc2FtZSBhcyBpdHMgZGVuc2l0eSBncmFwaC4KIyMjMgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSwgeSA9IGNhcmF0KSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF9udW1iZXIocHJpY2UsIDIwKSkpCgpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gY2FyYXQpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKHByaWNlLCAxMDAwLGJvdW5kYXJ5ID0gMCkpKQpgYGAKIyMjMwpJdCBzZWVtcyB0aGF0IHdoZW4gdGhlIHByaWNlIGlzIGhpZ2gsIHRoZSBwcmljZSBkb2Vzbid0IGNvcnJlbGF0ZSB0byB0aGUgc2l6ZSBhbnltb3JlLiBNYXliZSBzb21lIG90aGVyIGZhY3RvcnMgaW5mbHVlbmNlcy4KCiMjIzQKYGBge3J9CmRpYW1vbmRzICU+JSAKICAjZmlsdGVyKGNhcmF0PDMpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBjdXQpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gcHJpY2UpKQpgYGAKUHJpY2UgY29ycmVsYXRlIHdpdGggY2FyYXQgbW9yZSB0aGFuIGN1dC4KYGBge3J9CmRpYW1vbmRzICU+JSAKICAjZmlsdGVyKGNhcmF0PDMpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgICBmYWNldF93cmFwKH5jdXQpCmBgYApNb3JlIGRpYW1vbmRzIHdpdGggbGFyZ2Ugc2l6ZSBmb3IgZmFpciBjdXQuIE90aGVyIHRoYW4gdGhhdCBub3QgbXVjaCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGRpZmZlcmVudCBjdXQuCgojIyM1CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoNCwgMTEpLCB5bGltID0gYyg0LCAxMSkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHkpKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDQsIDExKSwgeWxpbSA9IGMoNCwgMTEpKQpgYGAKQmVjYXVzZSBvdXRsaWVycyBhcmUgbm90IHNob3duIGluIGJpbm5lZCBwbG90cy4KCiMjUGF0dGVybnMgYW5kIG1vZGVscwpDb3VsZCB0aGlzIHBhdHRlcm4gYmUgZHVlIHRvIGNvaW5jaWRlbmNlIChpLmUuIHJhbmRvbSBjaGFuY2UpPwpIb3cgY2FuIHlvdSBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGltcGxpZWQgYnkgdGhlIHBhdHRlcm4/CkhvdyBzdHJvbmcgaXMgdGhlIHJlbGF0aW9uc2hpcCBpbXBsaWVkIGJ5IHRoZSBwYXR0ZXJuPwpXaGF0IG90aGVyIHZhcmlhYmxlcyBtaWdodCBhZmZlY3QgdGhlIHJlbGF0aW9uc2hpcD8KRG9lcyB0aGUgcmVsYXRpb25zaGlwIGNoYW5nZSBpZiB5b3UgbG9vayBhdCBpbmRpdmlkdWFsIHN1Ymdyb3VwcyBvZiB0aGUgZGF0YT8KYGBge3J9CmdncGxvdChkYXRhID0gZmFpdGhmdWwpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMsIHkgPSB3YWl0aW5nKSkKYGBgCklmIHlvdSB0aGluayBvZiB2YXJpYXRpb24gYXMgYSBwaGVub21lbm9uIHRoYXQgY3JlYXRlcyB1bmNlcnRhaW50eSwgY292YXJpYXRpb24gaXMgYSBwaGVub21lbm9uIHRoYXQgcmVkdWNlcyBpdC4KCmBgYHtyfQpsaWJyYXJ5KG1vZGVscikKCm1vZCA8LSBsbShsb2cocHJpY2UpIH4gbG9nKGNhcmF0KSwgZGF0YSA9IGRpYW1vbmRzKQoKZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBhZGRfcmVzaWR1YWxzKG1vZCkgJT4lIAogIG11dGF0ZShyZXNpZCA9IGV4cChyZXNpZCkpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcmVzaWQpKQpgYGAKCnJlbGF0aXZlIHRvIHRoZWlyIHNpemUsIGJldHRlciBxdWFsaXR5IGRpYW1vbmRzIGFyZSBtb3JlIGV4cGVuc2l2ZS4KYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSByZXNpZCkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBmYWl0aGZ1bCwgbWFwcGluZyA9IGFlcyh4ID0gZXJ1cHRpb25zKSkgKyAKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4yNSkKCmdncGxvdChmYWl0aGZ1bCwgYWVzKGVydXB0aW9ucykpICsgCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMjUpCmBgYApodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy8KCg==